package com.atguigu.daijia.map.service.impl;

import com.atguigu.daijia.common.constant.RedisConstant;
import com.atguigu.daijia.common.constant.SystemConstant;
import com.atguigu.daijia.common.result.Result;
import com.atguigu.daijia.common.util.LocationUtil;
import com.atguigu.daijia.driver.client.DriverInfoFeignClient;
import com.atguigu.daijia.map.config.OrderServiceLocationRepository;
import com.atguigu.daijia.map.service.LocationService;
import com.atguigu.daijia.model.entity.driver.DriverInfo;
import com.atguigu.daijia.model.entity.driver.DriverSet;
import com.atguigu.daijia.model.entity.map.OrderServiceLocation;
import com.atguigu.daijia.model.form.map.OrderServiceLocationForm;
import com.atguigu.daijia.model.form.map.SearchNearByDriverForm;
import com.atguigu.daijia.model.form.map.UpdateDriverLocationForm;
import com.atguigu.daijia.model.form.map.UpdateOrderLocationForm;
import com.atguigu.daijia.model.vo.map.NearByDriverVo;
import com.atguigu.daijia.model.vo.map.OrderLocationVo;
import com.atguigu.daijia.model.vo.map.OrderServiceLastLocationVo;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.*;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class LocationServiceImpl implements LocationService {

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private DriverInfoFeignClient driverInfoFeignClient;
    @Autowired
    private OrderServiceLocationRepository orderServiceLocationRepository;
    @Autowired
    private MongoTemplate mongoTemplate;

    @Override
    public Boolean updateDriverLocation(UpdateDriverLocationForm updateDriverLocationForm) {
        Point point = new Point(updateDriverLocationForm.getLongitude().doubleValue(),
                updateDriverLocationForm.getLatitude().doubleValue());
        //添加到redis里面
        redisTemplate.opsForGeo().add(RedisConstant.DRIVER_GEO_LOCATION,
                point,
                updateDriverLocationForm.getDriverId().toString());
        return true;
    }

    @Override
    public Boolean removeDriverLocation(Long driverId) {
        redisTemplate.opsForGeo().remove(RedisConstant.DRIVER_GEO_LOCATION, driverId.toString());
        return true;
    }

    @Override
    public List<NearByDriverVo> searchNearByDriver(SearchNearByDriverForm searchNearByDriverForm) {
        Point point=new Point(searchNearByDriverForm.getLongitude().doubleValue(), searchNearByDriverForm.getLatitude().doubleValue());
        //定义距离，5公里
        Distance distance = new Distance(SystemConstant.NEARBY_DRIVER_RADIUS,
                RedisGeoCommands.DistanceUnit.KILOMETERS);
        //创建circle对象，point  distance
        Circle circle = new Circle(point,distance);

        //定义GEO参数，设置返回结果包含内容
        RedisGeoCommands.GeoRadiusCommandArgs args =
                RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
                        .includeDistance()  //包含距离
                        .includeCoordinates() //包含坐标
                        .sortAscending(); //升序

        GeoResults<RedisGeoCommands.GeoLocation<String>> result =
                redisTemplate.opsForGeo().radius(RedisConstant.DRIVER_GEO_LOCATION, circle, args);

        //2 查询redis最终返回list集合
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> content = result.getContent();

        //3 对查询list集合进行处理
        // 遍历list集合，得到每个司机信息
        // 根据每个司机个性化设置信息判断
        List<NearByDriverVo> list = new ArrayList<>();
        if(!CollectionUtils.isEmpty(content)) {
            Iterator<GeoResult<RedisGeoCommands.GeoLocation<String>>> iterator = content.iterator();
            while(iterator.hasNext()) {
                GeoResult<RedisGeoCommands.GeoLocation<String>> item = iterator.next();

                //获取司机id
                Long driverId = Long.parseLong(item.getContent().getName());

                //远程调用，根据司机id个性化设置信息
                Result<DriverSet> driverSetResult = driverInfoFeignClient.getDriverSet(driverId);
                DriverSet driverSet = driverSetResult.getData();

                //判断订单里程order_distance
                BigDecimal orderDistance = driverSet.getOrderDistance();
                //orderDistance==0，司机没有限制的
                //如果不等于0 ，比如30，接单30公里代驾订单。
                //接单距离 - 当前单子距离  < 0,不复合条件
                // 30          35
                if(orderDistance.doubleValue() != 0
                        && orderDistance.subtract(searchNearByDriverForm.getMileageDistance()).doubleValue()<0) {
                    continue;
                }

                //判断接单里程 accept_distance
                //当前接单距离
                BigDecimal currentDistance =
                        new BigDecimal(item.getDistance().getValue()).setScale(2, RoundingMode.HALF_UP);

                BigDecimal acceptDistance = driverSet.getAcceptDistance();
                if(acceptDistance.doubleValue() !=0
                        && acceptDistance.subtract(currentDistance).doubleValue()<0) {
                    continue;
                }

                //封装复合条件数据
                NearByDriverVo nearByDriverVo = new NearByDriverVo();
                nearByDriverVo.setDriverId(driverId);
                nearByDriverVo.setDistance(currentDistance);
                list.add(nearByDriverVo);

            }
        }
        return list;
    }

    @Override
    public Boolean updateOrderLocationToCache(UpdateOrderLocationForm updateOrderLocationForm) {
        OrderLocationVo orderLocationVo = new OrderLocationVo();
        orderLocationVo.setLongitude(updateOrderLocationForm.getLongitude());
        orderLocationVo.setLatitude(updateOrderLocationForm.getLatitude());
        redisTemplate.opsForValue().set(RedisConstant.UPDATE_ORDER_LOCATION + updateOrderLocationForm.getOrderId(), orderLocationVo);
        return true;
    }

    @Override
    public OrderLocationVo getCacheOrderLocation(Long orderId) {
        String key = RedisConstant.UPDATE_ORDER_LOCATION + orderId;
        return (OrderLocationVo) redisTemplate.opsForValue().get(key);
    }

    @Override
    public Boolean saveOrderServiceLocation(List<OrderServiceLocationForm> orderLocationServiceFormList) {
    // 定义一个OrderServiceLocation类型的列表，用于存储转换后的服务位置信息
        List<OrderServiceLocation> list = new ArrayList<>();
    // 遍历传入的OrderServiceLocationForm列表
        for (OrderServiceLocationForm orderServiceLocationForm : orderLocationServiceFormList) {
        // 创建一个新的OrderServiceLocation对象
            OrderServiceLocation orderServiceLocation = new OrderServiceLocation();
        // 使用BeanUtils工具类将orderServiceLocationForm的属性复制到orderServiceLocation中
            BeanUtils.copyProperties(orderServiceLocationForm, orderServiceLocation);
        // 为orderServiceLocation对象生成一个唯一的ID
            orderServiceLocation.setId(ObjectId.get().toString());
        // 设置orderServiceLocation对象的创建时间为当前时间
            orderServiceLocation.setCreateTime(new Date());
        // 将转换后的orderServiceLocation对象添加到list中
            list.add(orderServiceLocation);
        }
    // 调用orderServiceLocationRepository的saveAll方法，批量保存list中的所有服务位置信息
        orderServiceLocationRepository.saveAll(list);
    // 返回true，表示保存操作成功
        return true;
    }

    @Override
    public OrderServiceLastLocationVo getOrderServiceLastLocation(Long orderId) {
    // 定义一个查询对象
        Query query = new Query();
    // 向查询对象中添加条件，指定orderId等于传入的orderId
        query.addCriteria(Criteria.where("orderId").is(orderId));
    // 设置查询结果的排序方式，按createTime字段降序排序
        query.with(Sort.by(Sort.Order.desc("createTime")));
    // 限制查询结果的数量为1，即只获取最新的一条记录
        query.limit(1);
    // 使用MongoDB模板执行查询，获取符合条件的OrderServiceLocation对象
        OrderServiceLocation orderServiceLocation = mongoTemplate.findOne(query, OrderServiceLocation.class);
    // 创建一个OrderServiceLastLocationVo对象
        OrderServiceLastLocationVo orderServiceLastLocationVo=new OrderServiceLastLocationVo();
    // 将OrderServiceLocation对象的属性复制到OrderServiceLastLocationVo对象中
        //todo 这里源为null，待处理
        BeanUtils.copyProperties(orderServiceLocation, orderServiceLastLocationVo);
    // 返回OrderServiceLastLocationVo对象
        return orderServiceLastLocationVo;
    }

    @Override
    public BigDecimal calculateOrderRealDistance(Long orderId) {
        System.out.println(orderId+"-------------------------------------");
        OrderServiceLocation orderServiceLocation = new OrderServiceLocation();
        orderServiceLocation.setOrderId(orderId);
        Example<OrderServiceLocation> example = Example.of(orderServiceLocation);
        Sort sort= Sort.by(Sort.Order.asc("createTime"));
        List<OrderServiceLocation> orderServiceLocations = orderServiceLocationRepository.findAll(example, sort);

        double realDistance = 0;
        for (int i = 0; i < orderServiceLocations.size()-1; i++) {
            OrderServiceLocation location1 = orderServiceLocations.get(i);
            OrderServiceLocation location2 = orderServiceLocations.get(i + 1);

            //计算位置距离
            double distance = LocationUtil.getDistance(location1.getLatitude().doubleValue(),
                    location1.getLongitude().doubleValue(),
                    location2.getLatitude().doubleValue(),
                    location2.getLongitude().doubleValue());

            realDistance += distance;
        }
        return new BigDecimal(realDistance);
    }

}
